﻿#include <algorithm>
#include <cstdlib>
#include <print>
#include <utility>
#include <windows.h>


auto ManagerRun(LPCSTR exe, LPCSTR param, INT nShow = SW_SHOW) -> void;

constexpr DWORD SpeedStep = 100;
constexpr std::pair<DWORD, DWORD> SpeedLimits = {100, 1600};
DWORD MouseMoveDistance = 800;

auto main(int argc, char *argv[]) -> int
{
    // 如果是首次启动，则申请管理员权限。
    if (argc == 1)
    {
        // 隐藏控制台窗口
        ShowWindow(GetConsoleWindow(), SW_HIDE);
        ManagerRun(argv[0], "2");
        std::exit(EXIT_FAILURE);
    }
    // 第二次以管理员权限启动，开转。
    std::println("Running...");
    std::println("Press CAPSLOCK to rotate");
    std::println("Use UP/DOWN arrow keys to adjust speed({}-{})", SpeedLimits.first, SpeedLimits.second);
    std::println("Current speed: {}", MouseMoveDistance);
    while (true)
    {
        bool CapsLock_Pressed = GetKeyState(VK_CAPITAL) & 0x8000;
        if (CapsLock_Pressed)
        {
            // 模拟移动鼠标，向右
            mouse_event(MOUSEEVENTF_MOVE, MouseMoveDistance, 0, 0, 0);
        }
        // 检测方向键输入
        if (GetAsyncKeyState(VK_UP) & 0x8000)
        {
            MouseMoveDistance = std::min<DWORD>(MouseMoveDistance + SpeedStep, SpeedLimits.second);
            std::println("Speed increased: {}", MouseMoveDistance);
            Sleep(50);
        }
        else if (GetAsyncKeyState(VK_DOWN) & 0x8000)
        {
            MouseMoveDistance = std::max<DWORD>(MouseMoveDistance - SpeedStep, SpeedLimits.first);
            std::println("Speed decreased: {}", MouseMoveDistance);
            Sleep(50);
        }

        Sleep(20);
    }
}

// 以管理员权限重启程序
// 代码来自 https://www.codeleading.com/article/21972967069/
auto ManagerRun(LPCSTR exe, LPCSTR param, INT nShow) -> void
{
    SHELLEXECUTEINFO ShExecInfo;

    ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
    ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
    ShExecInfo.hwnd = nullptr;
    ShExecInfo.lpVerb = "runas";
    ShExecInfo.lpFile = exe;
    ShExecInfo.lpParameters = param;
    ShExecInfo.lpDirectory = nullptr;
    ShExecInfo.nShow = nShow;
    ShExecInfo.hInstApp = nullptr;

    ShellExecuteEx(&ShExecInfo);

    CloseHandle(ShExecInfo.hProcess);
    return;
}
